﻿using System;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
using System.Web;
using Microsoft.Office.Server.ActivityFeed;
using Microsoft.Office.Server.UserProfiles;
using System.Security.Principal;
using System.Collections.Generic;
using System.Reflection;
using System.Data.SqlClient;
using System.Text;
using Microsoft.SharePoint.Administration;
using eGov.SharePoint.MyPeeps.Properties;

namespace eGov.SharePoint.MyPeeps.EventReceivers
{
    /// <summary>
    /// List Item Events
    /// </summary>
    public class ContentActionFeedEventReceiver : SPItemEventReceiver
    {
        #region Constants

        public static string EventReceiverClassName = typeof(ContentActionFeedEventReceiver).FullName;
        public static string EventReceiverAssemblyName = typeof(ContentActionFeedEventReceiver).Assembly.FullName;

        #endregion

        #region ULS Prep

        private static SPDiagnosticsArea logArea;
        private static SPDiagnosticsService logService = SPDiagnosticsService.Local;
        protected static SPDiagnosticsArea LogArea
        {
            get
            {
                if (logArea == null)
                    logArea = new SPDiagnosticsArea("eGov.SharePoint.MyPeeps", new List<SPDiagnosticsCategory>
                    { 
                        new SPDiagnosticsCategory("ContentActionFeedEventReceiver", TraceSeverity.High, EventSeverity.Error),
                        new SPDiagnosticsCategory("ContentActionFeedEventReceiver", TraceSeverity.Verbose, EventSeverity.Information) 
                    });
                return logArea;
            }
        }
        protected static SPDiagnosticsCategory logCategory_Error
        {
            // Indexing if used starts from 1
            get { return LogArea.Categories[1]; }
        }
        protected static SPDiagnosticsCategory logCategory_Information
        {
            // Indexing if used starts from 1
            get { return LogArea.Categories[2]; }
        }

        #endregion

        /// <summary>
        /// An item was added.
        /// </summary>
        public override void ItemAdded(SPItemEventProperties properties)
        {
            base.ItemAdded(properties);
            using (SPSite site = properties.OpenSite())
            {
                SPServiceContext spContext = SPServiceContext.GetContext(site);

                string ActivityTypeName, ActivityTemplateName, Value;
                Link Link, Link2;
                GetContentActionFeedVariables(site, properties, out ActivityTypeName, out ActivityTemplateName, out Value, out Link, out Link2);

                UserProfile meProfile = GetUserProfile(spContext, site.RootWeb);
                List<ActivityEvent> activityEvents = new List<ActivityEvent>();

                // Generate new feed
                activityEvents.Add(this.GenerateActivityEvent(site, meProfile, ActivityTypeName, ActivityTemplateName, Value, Link, Link2));

                // Multicast custom feeds
                MulticastPublishedEvents(site, activityEvents, new MinimalPerson(meProfile).AccountName);
            }

        }

        /// <summary>
        /// An item was updated.
        /// </summary>
        public override void ItemUpdated(SPItemEventProperties properties)
        {
            base.ItemUpdated(properties);
            using (SPSite site = properties.OpenSite())
            {
                SPServiceContext spContext = SPServiceContext.GetContext(site);

                string ActivityTypeName, ActivityTemplateName, Value;
                Link Link, Link2;
                GetContentActionFeedVariables(site, properties, out ActivityTypeName, out ActivityTemplateName, out Value, out Link, out Link2);

                UserProfile meProfile = GetUserProfile(spContext, site.RootWeb);
                //UserProfileManager pm = new UserProfileManager(true);
                //UserProfile meProfile = pm.GetUserProfile(true);
                List<ActivityEvent> activityEvents = new List<ActivityEvent>();

                // Generate new feed
                this.GenerateActivityEvent(site, meProfile, ActivityTypeName, ActivityTemplateName, Value, Link, Link2);

                // Multicast custom feeds
                MulticastPublishedEvents(site, activityEvents, new MinimalPerson(meProfile).AccountName);
            }
        }

        /// <summary>
        /// An item was deleted.
        /// </summary>
        public override void ItemDeleted(SPItemEventProperties properties)
        {
            base.ItemDeleted(properties);
            using (SPSite site = properties.OpenSite())
            {
                SPServiceContext spContext = SPServiceContext.GetContext(site);

                UserProfile meProfile = GetUserProfile(spContext, site.RootWeb);
                List<ActivityEvent> activityEvents = new List<ActivityEvent>();

                Link listLink = new Link()
                {
                    Href = site.Url + "/" + properties.List.DefaultViewUrl,
                    //Href = SPUtility.GetFullUrl(site, properties.List.DefaultViewUrl),
                    Name = properties.ListTitle
                };
                string Value = properties.BeforeUrl.Substring(properties.BeforeUrl.LastIndexOf("/") + 1);

                // Generate new feed
                activityEvents.Add(this.GenerateActivityEvent(site, meProfile, Settings.Default.ActivityTitle_ContentDelete, Settings.Default.ActivityTemplateTitle_ContentDelete, Value, listLink, null));

                // Multicast custom feeds
                MulticastPublishedEvents(site, activityEvents, new MinimalPerson(meProfile).AccountName);

            }
        }

        #region Helper Methods

        protected void GetContentActionFeedVariables(SPSite site, SPItemEventProperties properties,
            out string ActivityTypeName, out string ActivityTemplateName, out string Value, out Link Link, out Link Link2)
        {
            ActivityTypeName = null;
            ActivityTemplateName = null;
            Value = null; Link = null; Link2 = null;

            switch (properties.ListItem.ContentType.Name)
            {
                case "Document":
                    {
                        switch (properties.EventType)
                        {
                            case SPEventReceiverType.ItemAdded:
                                {
                                    ActivityTypeName = Settings.Default.ActivityTitle_ContentNew;
                                    ActivityTemplateName = Settings.Default.ActivityTemplateTitle_DocumentNew;
                                    break;
                                }
                            case SPEventReceiverType.ItemUpdated:
                                {
                                    ActivityTypeName = Settings.Default.ActivityTitle_ContentModify;
                                    ActivityTemplateName = Settings.Default.ActivityTemplateTitle_DocumentModify;
                                    break;
                                }
                        }

                        Link = new Link()
                        {
                            Href = site.Url + "/" + properties.AfterUrl,
                            //SPUtility.GetFullUrl(site, "/" + properties.AfterUrl),
                            Name = String.IsNullOrEmpty(properties.ListItem.Title) ? properties.ListItem.Name : properties.ListItem.Title
                        };
                        Link2 = new Link()
                        {
                            //Href = site.Url + "/" + properties.List.DefaultViewUrl,
                            Href = SPUtility.GetFullUrl(site, properties.List.DefaultViewUrl),
                            Name = properties.ListTitle
                        };

                        break;
                    }
                case "Picture":
                    {
                        switch (properties.EventType)
                        {
                            case SPEventReceiverType.ItemAdded:
                                {
                                    ActivityTypeName = Settings.Default.ActivityTitle_ContentNew;
                                    ActivityTemplateName = Settings.Default.ActivityTemplateTitle_PictureNew;

                                    Link = new Link()
                                    {
                                        Href = site.Url + "/" + properties.List.DefaultViewUrl,
                                        //Href = SPUtility.GetFullUrl(site, properties.List.DefaultViewUrl),
                                        Name = properties.ListTitle
                                    };

                                    // Picture Thumbnail
                                    Value = properties.WebUrl + "/" + properties.ListTitle + "/_t/";
                                    string pictureName = properties.ListItem.Name;
                                    string PictureThumbnail = properties.ListItem.Name.Substring(0, pictureName.LastIndexOf('.')) + "_" +
                                        properties.ListItem.Name.Substring(pictureName.LastIndexOf('.') + 1) + ".jpg";
                                    Value += PictureThumbnail;

                                    break;
                                }
                            case SPEventReceiverType.ItemUpdated:
                                {
                                    ActivityTypeName = Settings.Default.ActivityTitle_ContentModify;
                                    ActivityTemplateName = Settings.Default.ActivityTemplateTitle_PictureModify;

                                    Link = new Link()
                                    {
                                        Href = site.Url + "/" + properties.AfterUrl,
                                        //Href = SPUtility.GetFullUrl(site, "/" + properties.AfterUrl),
                                        Name = String.IsNullOrEmpty(properties.ListItem.Title) ? properties.ListItem.Name : properties.ListItem.Title
                                    };
                                    Link2 = new Link()
                                    {
                                        //Href = site.Url + "/" + properties.List.DefaultViewUrl,
                                        Href = SPUtility.GetFullUrl(site, properties.List.DefaultViewUrl),
                                        Name = properties.ListTitle
                                    };

                                    break;
                                }
                        }

                        break;
                    }
            }
        }

        protected UserProfile GetUserProfile(SPServiceContext spContext, SPWeb spWeb)
        {
            UserProfileManager usrProfileManager = new UserProfileManager(spContext);
            if (usrProfileManager.UserExists(spWeb.CurrentUser.LoginName))
                return usrProfileManager.GetUserProfile(spWeb.CurrentUser.LoginName);
            return null;
        }

        public List<long> MulticastPublishedEvents(SPSite site, List<ActivityEvent> activityEvents, string PublisherAccountName)
        {
            List<long> multicatedFeedIds = new List<long>();

            Guid SPSiteGuid = site.ID;
            if (activityEvents.Count == 0) return multicatedFeedIds;

            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite Site = new SPSite(SPSiteGuid))
                {
                    HttpContext currentContext = HttpContext.Current;
                    try
                    {
                        HttpContext.Current = null;
                        SPServiceContext spContext = SPServiceContext.GetContext(Site);

                        UserProfileManager userManager = new UserProfileManager(spContext);
                        string accPoolProfileName = WindowsIdentity.GetCurrent().Name;
                        UserProfile accPoolProfile = null;
                        if (userManager.UserExists(accPoolProfileName)) accPoolProfile = userManager.GetUserProfile(accPoolProfileName);
                        else userManager.CreateUserProfile(accPoolProfileName);

                        ActivityManager activityManager = new ActivityManager(accPoolProfile, spContext);

                        /// Method code
                        List<long> publishers = new List<long>();
                        foreach (ActivityEvent activityEvent in activityEvents)
                        {
                            if (!publishers.Contains(activityEvent.Owner.Id))
                                publishers.Add(activityEvent.Owner.Id);
                        }

                        Dictionary<long, MinimalPerson> owners;
                        Dictionary<long, List<MinimalPerson>> colleaguesOfOwners;
                        ActivityFeedGatherer.GetUsersColleaguesAndRights(activityManager, publishers, out owners, out colleaguesOfOwners);

                        Dictionary<long, List<ActivityEvent>> eventsPerOwner;
                        ActivityFeedGatherer.MulticastActivityEvents(activityManager, activityEvents, colleaguesOfOwners, out eventsPerOwner);

                        List<ActivityEvent> eventsToMulticast;
                        ActivityFeedGatherer.CollectActivityEventsToConsolidate(eventsPerOwner, out eventsToMulticast);

                        WriteEvents(activityManager, eventsToMulticast);

                        // End of method code
                        activityManager.Dispose();
                    }
                    catch (UnauthorizedAccessException securityEx)
                    {
                        logService.WriteTrace(100, logCategory_Error, TraceSeverity.High,
                      String.Format(@"Failed to publish custom activity feeds. Make sure application pool account have admin permissions for the user profile service.
                        Exception message: {0} \r\n Stack Trace: \r\n {1}", new object[] { securityEx.Message, securityEx.StackTrace }), null);
                    }
                    catch (SPException sharepointEx)
                    {
                        logService.WriteTrace(100, logCategory_Error, TraceSeverity.High,
                      String.Format("Failed to publish custom activity feeds. Exception message: {0} \r\n Stack Trace: \r\n {1}", new object[] { sharepointEx.Message, sharepointEx.StackTrace }), null);
                    }
                    finally
                    {
                        HttpContext.Current = currentContext;
                    }
                }
            });

            return multicatedFeedIds;
        }

        private void WriteEvents(ActivityManager ActManager, List<ActivityEvent> events)
        {
            int startIndex = 0;
            while (startIndex + ActManager.MaxEventsPerBatch < events.Count)
            {
                ActivityFeedGatherer.BatchWriteActivityEvents(events, startIndex, ActManager.MaxEventsPerBatch);
                startIndex += ActManager.MaxEventsPerBatch;
            }

            ActivityFeedGatherer.BatchWriteActivityEvents(events, startIndex, events.Count - startIndex);
        }

        public ActivityEvent GenerateActivityEvent(SPSite site, UserProfile userProfile,
            string ActivityTypeName, string ActivityTemplateName, string Value, Link Link, Link Link2)
        {
            ActivityEvent activityEvent = null;
            Guid SPSiteGuid = site.ID;

            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite Site = new SPSite(SPSiteGuid))
                {
                    HttpContext currentContext = HttpContext.Current;
                    try
                    {
                        HttpContext.Current = null;
                        SPServiceContext spContext = SPServiceContext.GetContext(Site);

                        UserProfileManager userManager = new UserProfileManager(spContext);
                        string accPoolProfileName = WindowsIdentity.GetCurrent().Name;
                        UserProfile accPoolProfile = null;
                        if (userManager.UserExists(accPoolProfileName)) accPoolProfile = userManager.GetUserProfile(accPoolProfileName);
                        else userManager.CreateUserProfile(accPoolProfileName);

                        ActivityManager activityManager = new ActivityManager(accPoolProfile, spContext);
                        /// Method code
                        ActivityApplication actApplication = activityManager.ActivityApplications[Settings.Default.ActivityApplicationTitle_eGovCustomFeeds];
                        ActivityType actType = actApplication.ActivityTypes[ActivityTypeName];

                        Entity owner = new MinimalPerson(userProfile).CreateEntity(activityManager);
                        Entity publisher = new MinimalPerson(userProfile).CreateEntity(activityManager);

                        activityEvent = ActivityEvent.CreateActivityEvent(activityManager, actType.ActivityTypeId, owner, publisher);
                        activityEvent.Name = ActivityTemplateName;
                        activityEvent.ItemPrivacy = (int)Privacy.Public;
                        activityEvent.Owner = owner;
                        activityEvent.Publisher = publisher;
                        activityEvent.Value = Value;
                        activityEvent.Link = Link;
                        activityEvent.Link2 = Link2;
                        activityEvent.Commit();

                        /// End of method code
                        activityManager.Dispose();
                    }
                    catch (UnauthorizedAccessException securityEx)
                    {
                        logService.WriteTrace(100, logCategory_Error, TraceSeverity.High,
                      String.Format(@"Failed to create custom activity feed. Make sure application pool account have admin permissions for the user profile service.
                        Exception message: {0} \r\n Stack Trace: \r\n {1}", new object[] { securityEx.Message, securityEx.StackTrace }), null);
                    }
                    catch (SPException sharepointEx)
                    {
                        logService.WriteTrace(100, logCategory_Error, TraceSeverity.High,
                      String.Format("Failed to create custom activity feed. Exception message: {0} \r\n Stack Trace: \r\n {1}", new object[] { sharepointEx.Message, sharepointEx.StackTrace }), null);
                    }
                    finally
                    {
                        HttpContext.Current = currentContext;
                    }
                }
            });

            return activityEvent;
        }

        #endregion

        #region Enable Service On List

        public static bool ActivateServiceOnList(SPList list)
        {
            try
            {
                //Add the new Event Receiver to the collection
                if (IsEventReceiverAttached(list))
                    return true;

                list.EventReceivers.Add(SPEventReceiverType.ItemAdded, EventReceiverAssemblyName, EventReceiverClassName);
                list.EventReceivers.Add(SPEventReceiverType.ItemDeleted, EventReceiverAssemblyName, EventReceiverClassName);
                list.EventReceivers.Add(SPEventReceiverType.ItemUpdated, EventReceiverAssemblyName, EventReceiverClassName);

                logService.WriteTrace(100, logCategory_Information, TraceSeverity.Verbose, String.Format("Custom feeds for List: successfully activated.", new object[] { list.Title }), null);
                return true;
            }
            catch (System.Exception ex)
            {
                logService.WriteTrace(100, logCategory_Error, TraceSeverity.High,
                    String.Format("Failed to activate custom feeds for List: {0}. Exception message: {1} \r\n Stack Trace: \r\n {2}", new object[] { list.Title, ex.Message, ex.StackTrace }), null);
            }
            return false;
        }

        public static void DeactivateServiceOnList(SPList list)
        {
            try
            {
                //Find all receivers in the collection and remove it
                List<SPEventReceiverDefinition> toDelete = new List<SPEventReceiverDefinition>();
                foreach (SPEventReceiverDefinition eventReceiver in list.EventReceivers)
                    if (eventReceiver.Class == EventReceiverClassName) toDelete.Add(eventReceiver);

                foreach (SPEventReceiverDefinition eventReceiver in toDelete)
                    eventReceiver.Delete();

                logService.WriteTrace(100, logCategory_Information, TraceSeverity.Verbose, String.Format("Custom feeds for List: successfully deactivated.", new object[] { list.Title }), null);
            }
            catch (System.Exception ex)
            {
                logService.WriteTrace(100, logCategory_Error, TraceSeverity.High,
                    String.Format("Failed to deactivate custom feeds for List: {0}. Exception message: {1} \r\n Stack Trace: \r\n {2}", new object[] { list.Title, ex.Message, ex.StackTrace }), null);
            }
        }

        public static bool IsEventReceiverAttached(SPList list)
        {
            if (list != null && list.EventReceivers != null)
            {
                foreach (SPEventReceiverDefinition eventReceiver in list.EventReceivers)
                    if (eventReceiver.Class == EventReceiverClassName)
                        return true;
            }
            return false;
        }

        #endregion

    }
}
